﻿
Imports System.IO
Imports System.Threading
Imports std
Imports xmlctrl
Imports xhtmlctrl
Imports webctrl

#Const tcpServer2 = True

Public Module rlogserve

    Public Class arguments
        Public Const tcpport As String = "tcpport"
        Public Const httpport As String = "httpport"
        Public Const logfile As String = "logfile"
        Public Const flushtimes As String = "flushtimes"

        Public Class defaultValues
            Public Const tcpport As Int32 = 29900
            Public Const httpport As Int32 = 80
            Public Shared ReadOnly logfile As String = defaultLogPath() + "\remotelog\" _
                                    + shortTime(character.underline, character.minusSign, character.minusSign) + ".log"
#If DEBUG Then
            Public Const flushtimes As int64 = 1
#Else
            Public Const flushtimes As Int64 = 32
#End If
        End Class
    End Class

    Public Class logkeys
        Public Const app As String = "app"
        Public Const msg As String = "msg"
        Public Const errorType As String = "etp"
        Public Const errorTypeChar As String = "etc"
    End Class

    Public Class logreply
        Public Const success As String = "suc"
        Public Const failed As String = "fld"
    End Class

    Public writer As autoflushFileStream2 = Nothing
#If tcpServer2 Then
    Public WithEvents tcpServer As tcpServer2 = Nothing
#Else
    Public WithEvents tcpServer As tcpServerAngel = Nothing
#End If
    Public WithEvents httpServer As httpServer2 = Nothing
    Public writetally As tally = Nothing

    Public Sub unhandledException(ByVal ex As Exception)
        shutdown()
    End Sub

    Public Sub shutdown()
        httpServer.stop()
        tcpServer.stop()
        writer.Flush()
        writer.Close()
        finishmain()
        forceQuit(0)
    End Sub

    Public Sub main(ByVal iargs() As String)
        startmain(AddressOf unhandledException)
        Dim args As map(Of String, vector(Of String)) = Nothing
        argmgr.parse(iargs, args, True)

        Dim tcpport As Int32
        If Not argmgr.getfirstvalue(args, arguments.tcpport, tcpport, True) Then
            tcpport = arguments.defaultValues.tcpport
            raiseError("cannot get tcpport defination in arguments, use default value " + Convert.ToString(tcpport))
        End If
#If tcpServer2 Then
        tcpServer = New tcpServer2()
#Else
        tcpServer = New tcpServerAngel()
#End If
        tcpServer.port = tcpport

        httpServer = New httpServer2()
        If argmgr.containsValue(args, arguments.httpport, True) Then
            httpServer.addports(args(arguments.httpport).ToString(xmlcontrolHttpServer.portsSeperators(0)))
        Else
            httpServer.addport(arguments.defaultValues.httpport)
            raiseError("cannot get httpport defination in arguments, use default value " _
                       + Convert.ToString(arguments.defaultValues.httpport))
        End If

        Dim logfile As String = Nothing
        If Not argmgr.getfirstvalue(args, arguments.logfile, logfile, True) Then
            logfile = arguments.defaultValues.logfile
            raiseError("cannot get logfile defination in arguments, use default value " + logfile)
        End If
        Dim dir As String = Nothing
        dir = parentpath(logfile)
        If Not Directory.Exists(dir) Then
            Directory.CreateDirectory(dir)
        End If
        writer = New autoflushFileStream2(logfile, FileMode.Append, FileAccess.Write, FileShare.Read)

        Dim flushtimes As Int64
        If Not argmgr.getfirstvalue(args, arguments.flushtimes, flushtimes, True) Then
            flushtimes = arguments.defaultValues.flushtimes
            raiseError("cannot get flushtimes defination in arguments, use default value " _
                       + Convert.ToString(flushtimes))
        End If
        writetally = New tally(flushtimes)

        tcpServer.start()
        httpServer.start()

        While Not consoleExtSignal()
            sleep()
        End While

        raiseError("caught console cancel key signal, shutdown ...")

        shutdown()
    End Sub

    Private Sub tcpServer_receive(ByVal handle As tcpServer.talkHandle) Handles tcpServer.receive
        assert(Not handle Is Nothing, "handle is nothing when tcpServer_receive.")
        Dim receiveData As nettransData = handle.receiveData
        Dim pairs As map(Of String, String) = Nothing
        If Not nettransDataParser.keyvaluepair(receiveData, pairs) Then
            raiseError("cannot parse keyvalue pair from receiveData, ignore request, " + receiveData.display() _
                       , errorHandle.errorType.warning)

            Return
        End If

        Dim senddata As nettransData = Nothing
        senddata = New nettransData()
        If writeremotelog(pairs) Then
            senddata.insert(logreply.success)
        Else
            senddata.insert(logreply.failed)
        End If

        If Not handle.return(senddata) Then
            raiseError("cannot reply data to client, " + senddata.display, errorHandle.errorType.system)
        End If
        senddata.clear()
    End Sub

    Private Sub httpServer_receive(ByVal context As httpContext) Handles httpServer.receive
        assert(Not context Is Nothing, "context is nothing when httpServer_receive.")

        If writeremotelog(context.url.query) Then
            context.writer().Write(logreply.success)
            context.setstatuscode(Net.HttpStatusCode.OK, "OK")
            context.setcontentType(network.http.contentType.texthtmlUtf8)
        Else
            context.writer().Write(logreply.failed)
            context.setstatuscode(Net.HttpStatusCode.OK, "OK")
            context.setcontentType(network.http.contentType.texthtmlUtf8)
        End If
    End Sub

    Public Function writeremotelog(ByVal pairs As map(Of String, String)) As Boolean
        assert(Not pairs Is Nothing, "pairs is nothing when writeremotelog.")
        Dim app As String = Nothing
        Dim msg As String = Nothing
        Dim errType As errorHandle.errorType = errorHandle.errorType.information
        Dim errTypeChar As Char = character.null

        Dim it As map(Of String, String).iterator = Nothing
        it = pairs.find(logkeys.app)
        If it = pairs.end() Then
            raiseError("do not get " + logkeys.app + " value from request, ignore.", errorHandle.errorType.user)
            Return False
        Else
            copy(app, (+it).second)
        End If

        it = pairs.find(logkeys.msg)
        If it = pairs.end() Then
            raiseError("do not get " + logkeys.msg + " value from request, ignore.", errorHandle.errorType.user)
            Return False
        Else
            copy(msg, (+it).second)
        End If

        it = pairs.find(logkeys.errorType)
        If it = pairs.end() Then
            raiseError("do not get " + logkeys.errorType + " value from request, use default value." _
                       , errorHandle.errorType.user)
            errType = errorHandle.errorType.information
        Else
            Dim errTypeInt As Int32
            If convertor.toint32((+it).second, errTypeInt) _
                AndAlso errTypeInt > errorHandle.errorType.first _
                AndAlso errTypeInt < errorHandle.errorType.last Then
                errType = errTypeInt
            Else
                raiseError("do not get valid " + logkeys.errorType + " value from request, use default value." _
                            , errorHandle.errorType.user)
                errType = errorHandle.errorType.information
            End If
        End If

        it = pairs.find(logkeys.errorTypeChar)
        If it = pairs.end() Then
            raiseError("do not get " + logkeys.errorTypeChar + " value from request, use default value." _
                        , errorHandle.errorType.user)
            errTypeChar = character.null
        Else
            errTypeChar = (+it).second(0)
        End If

        Return writeremotelog(app, msg, errType, errTypeChar)
    End Function

    Public Function writeremotelog(ByVal app As String, ByVal msg As String _
                                   , Optional ByVal errType As errorHandle.errorType = errorHandle.errorType.information _
                                   , Optional ByVal errTypeChar As Char = character.null) As Boolean
        assert(Not writer Is Nothing, "writer is nothing when writeremotelog.")

        Dim errmsg As String = Nothing
        errmsg = patchupErrMsg("application " + app + ", " + msg, errType, errTypeChar)
        Dim bts() As Byte = Nothing
        bts = encoding.utf8.GetBytes(errmsg)
        SyncLock writer
            writer.Write(bts, 0, bts.Length())
            writer.Write(+(newline.utf8), 0, newline.utf8.size())
            If writetally.tick() Then
                writer.Flush()
            End If
        End SyncLock

        Return True
    End Function
End Module
